//! Parses the directory of built object files
//! and generates the multiboot2-compliant grub.cfg file.


extern crate getopts;

use getopts::Options;
use std::fs;
use std::io::Write;
use std::process;
use std::env;

fn main() -> Result<(), String> {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file path, e.g., \"/my/dir/grub.cfg\"", "OUTPUT_PATH");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(&args[1..]).map_err(|e| e.to_string())?;

    if matches.opt_present("h") {
        print_usage("cargo run -- ", opts);
        process::exit(0);
    }

    // Require input directory 
    let input_directory = match matches.free.len() {
        0 => return Err(format!("no input directory provided")),
        1 => matches.free[0].clone(), 
        _ => return Err(format!("Too many arguments entered")),
    };
    
    let grub_cfg_string = create_grub_cfg_string(input_directory)?;
    
    // Write to output file (if provided) 
    if matches.opt_present("o") {
        let output_file_path = matches.opt_str("o")
            .ok_or_else(|| String::from("failed to match output file argument."))?;
        write_content(grub_cfg_string, output_file_path);
    }
    // Otherwise, write to stdout by default
    else {
        println!("{}", grub_cfg_string);
    }

    Ok(())
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options] INPUT_DIRECTORY", program);
    print!("{}", opts.usage(&brief));
}

fn create_grub_cfg_string(input_directory: String) -> Result<String, String> {
    // Creates string to write to grub.cfg file by looking through all files in input_directory
    let mut content = String::new();
    
    let mut path_to_exe = std::env::current_exe().unwrap_or_default();
    // go up three directories to remove the "target/<build_mode>/name"
    path_to_exe.pop(); path_to_exe.pop(); path_to_exe.pop();

    content.push_str("### This file has been autogenerated, do not manually modify it!\n");
    content.push_str(&format!("### Generated by program: \"{}\"\n", path_to_exe.display()));
    content.push_str(&format!("### Input directory: \"{}\"\n\n", &input_directory));
    content.push_str("set timeout=0\n");
    content.push_str("set default=0\n\n");
    content.push_str("menuentry \"Theseus OS\" {\n");
    content.push_str("\tmultiboot2 /boot/kernel.bin \n");
    // Below is a priority-ordered list of resolutions.
    // Based on our testing, 4x3 aspect ratios are the most widely supported.
    content.push_str("\tset gfxpayload=1280x1024x32,1280x720x32,1024x768x32,640x480x32,auto \n");
    

    for path in fs::read_dir(input_directory).map_err(|e| e.to_string())? {
        let path = path.map_err(|e| e.to_string())?;
        let p = path.path();
        let file_name = p.file_name().and_then(|f| f.to_str()).ok_or_else(|| format!("Path error in path {:?}", path))?;
        content.push_str(&format!("\tmodule2 /modules/{0:50}\t\t{1:50}\n", file_name, file_name));
    }

    content.push_str("\n\tboot\n}\n");
    Ok(content)
}

fn write_content(content: String, output_file_path: String) {
    if let Ok(mut file) = fs::File::create(output_file_path) {
        if file.write(content.as_bytes()).is_ok(){ process::exit(0); }
    }
}
